Ext.define('sid.cv.RecordView', {
	extend: 'Ext.Component',
	requires: ['Ext.LoadMask', 'Ext.data.StoreMgr', 'Ext.XTemplate'],
	alias: 'widget.sid.cv.recordview',
	
	loadingText: 'Loading...',
	
	/**
	 * @cfg {String} emptyText
	 * The text to display in the view when there is no data to display (defaults to '').
	 */
	emptyText: '',
	
	/**
	 * @cfg {Boolean} deferEmptyText True to defer emptyText being applied until the store's first load
	 */
	deferEmptyText: true,
	
	fieldSelector: undefined,
	selectionListeners: undefined,
	
	tpl: undefined,
	tplId: undefined,
	
	store: undefined,
	recordId: undefined,
	
	record: undefined,
	
	initComponent: function() {
		if (!(Ext.isDefined(this.tpl) ^ Ext.isDefined(this.tplId))) {
			throw "sid.cv.RecordView requires tpl or tplId to be defined.";
		}
		
		if (!((Ext.isDefined(this.store) && Ext.isDefined(this.recordId)) ^ Ext.isDefined(this.record))) {
			throw "sid.cv.RecordView requires store and recordId, or record to be defined.";
		}
		
		sid.cv.RecordView.superclass.initComponent.call(this);
		
		if (Ext.isString(this.tpl) || Ext.isArray(this.tpl)) {
			this.tpl = new Ext.XTemplate(this.tpl);
		}
		
		if (Ext.isString(this.tplId)) {
			this.tpl = Ext.XTemplate.from(this.tplId);
		}
		
		this.addEvents('beforerefresh', 'refresh');
	},
	
	onRender: function() {
		var me = this;
		var loadingHeight = me.loadingHeight;
		var loadingText = me.loadingText;
		
		me.callParent(arguments);
		
		if (loadingText) {
			me.loadMask = new Ext.LoadMask(me.el, {
				msg: loadingText,
				
				listeners: {
					beforeshow: function(loadMask, el) {
						el.update('');
						if (Ext.isNumber(loadingHeight)) {
							el.setHeight(loadingHeight);
						}
					}
				}
			});
		}
	},
	
	onClick: function() {
	},
	
	setRecord: function(record, initial) {
		this.record = record;
		
		if (this.record) {
			this.recordId = this.record.getId();
			
			if (initial) {
				this.store = this.record.store;
			}
			else {
				this.bindStore(this.record.store, false);
			}
			
			if (!initial) {
				this.refresh();
			}
		}
	},
	
	afterRender: function() {
		var me = this;
		
		var listeners = {
			scope: me,
			click: me.onClick
		};
		
		me.callParent();
		
		me.mon(me.getTargetEl(), listeners);
		
		if (me.record) {
			me.setRecord(me.record, true);
		}
		
		if (me.store) {
			me.bindStore(me.store, true);
		}
	},
	
	/* The following methods are shamelessly duplicated from Ext.AbstractDataView. Ext.XTemplate does not understand
	 * model instances (yet), and so the data and all associations must be marshalled into vanilla objects. */
	prepareData: function(data, record) {
		if (record) {
			Ext.apply(data, this.prepareAssociatedData(record));
		}
		return data;
	},
	
	prepareAssociatedData: function(record, ids) {
		//we keep track of all of the internalIds of the models that we have loaded so far in here
		ids = ids || [];
		
		var associations = record.associations.items, associationCount = associations.length, associationData = {}, associatedStore, associatedName, associatedRecords, associatedRecord, associatedRecordCount, association, internalId, i, j;
		
		for (i = 0; i < associationCount; i++) {
			association = associations[i];
			
			//this is the hasMany store filled with the associated data
			associatedStore = record[association.storeName];
			
			//we will use this to contain each associated record's data
			associationData[association.name] = [];
			
			//if it's loaded, put it into the association data
			if (associatedStore && associatedStore.data.length > 0) {
				associatedRecords = associatedStore.data.items;
				associatedRecordCount = associatedRecords.length;
				
				//now we're finally iterating over the records in the association. We do this recursively
				for (j = 0; j < associatedRecordCount; j++) {
					associatedRecord = associatedRecords[j];
					internalId = associatedRecord.internalId;
					
					//when we load the associations for a specific model instance we add it to the set of loaded ids so that
					//we don't load it twice. If we don't do this, we can fall into endless recursive loading failures.
					if (ids.indexOf(internalId) == -1) {
						ids.push(internalId);
						
						associationData[association.name][j] = associatedRecord.data;
						Ext.apply(associationData[association.name][j], this.prepareAssociatedData(associatedRecord, ids));
					}
				}
			}
		}
		
		return associationData;
	},
	
	refresh: function() {
		var me = this;
		
		if (!me.rendered) {
			return;
		}
		
		me.fireEvent('beforerefresh', me);
		
		var el = me.getTargetEl();
		
		el.update('');
		
		if (!me.record) {
			if (!me.deferEmptyText || me.hasSkippedEmptyText) {
				el.update(me.emptyText);
			}
		}
		else {
			me.tpl.overwrite(el, me.prepareData(me.record.data, me.record));
		}
		
		if (me.fieldSelector && me.selectionListeners) {
			Ext.each(Ext.fly(el).query(me.fieldSelector), function(node) {
				console.log(node);
				Ext.fly(node).on(me.selectionListeners);
			});
		}
		
		me.hasSkippedEmptyText = true;
		
		me.fireEvent('refresh', me);
	},
	
	onDataChanged: function() {
		console.log('onDataChanged');
		if (this.record) {
			return;
		}
		
		this.record = this.store.getById(this.recordId);
		this.refresh();
	},
	
	onAdd: function() {
		console.log('onAdd');
		if (this.record) {
			return;
		}
		
		this.record = this.store.getById(this.recordId);
		
		this.refresh();
	},
	
	onRemove: function(store, record) {
		console.log('onRemove');
		if (!this.record) {
			return;
		}
		
		if (this.record == record) {
			this.record = undefined;
			this.refresh();
		}
	},
	
	onUpdate: function(store, record) {
		console.log('onUpdate');
		if (this.record == record) {
			this.refresh();
			return;
		}
	},
	
	onClear: function() {
		console.log('onClear');
		this.record = undefined;
		this.refresh();
	},
	
	bindStore: function(store, initial) {
		if (!initial && this.store) {
			if (store !== this.store && this.store.autoDestroy) {
				this.store.destroy();
			}
			else {
				this.mun(this.store, {
					scope: this,
					datachanged: this.onDataChanged,
					add: this.onAdd,
					remove: this.onRemove,
					update: this.onUpdate,
					clear: this.onClear
				});
			}
			
			if (!store) {
				if (this.loadMask) {
					this.loadMask.bindStore(null);
				}
				
				this.store = null;
			}
		}
		
		if (store) {
			store = Ext.data.StoreMgr.lookup(store);
			this.mon(store, {
				scope: this,
				datachanged: this.onDataChanged,
				add: this.onAdd,
				remove: this.onRemove,
				update: this.onUpdate,
				clear: this.onClear
			});
			
			if (this.loadMask) {
				this.loadMask.bindStore(store);
			}
		}
		
		this.store = store;
		
		if (store) {
			this.refresh();
		}
	}
});
